/**
 * 
 */
package ch.gje.playios.server.handler;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import ch.gje.playios.server.HTTPConnection;

/**
 * Abstract Query Handler
 * 
 * @author jannis <jannis@gje.ch>
 * 
 */
public abstract class RequestHandler {

    /**
     * HTTP connection
     */
    protected HTTPConnection connection;
    /**
     * HTTP status code
     */
    private int responseStatusCode = 500;
    /**
     * HTTP status description
     */
    private String responseStatusDescription = "Internal Server Error";
    /**
     * HTTP headers to send by server
     */
    private Map<String, String> responseHeaders;
    /**
     * If the response headers are sent
     */
    private boolean responseHeadersSent = false;

    /**
     * Constructor
     *
     * @param connection
     *            the HTTP connection
     */
    public RequestHandler(HTTPConnection connection) {
        this.connection = connection;
        responseHeaders = new HashMap<String, String>();
        addDefaultHeaders();
    }

    /**
     * adds the default headers for HTTP
     */
    private void addDefaultHeaders() {
        responseHeaders.put("Server", "play-ios/1.0");
        responseHeaders.put("Content-Type", "text/text");
        responseHeaders.put("Connection", "close");
    }

    /**
     * Handles the HTTP request
     */
    public void handle() {
        try {
            handleQuery();
        } catch (Exception e) {
            e.printStackTrace();
            /* kill connection slowly */
            if (!connection.isClosed()) {
                if (!areHeadersSent()) {
                    try {
                        new InternalServerErrorHandler(connection);
                    } catch (Exception ee) {
                        try {
                            connection.close();
                        } catch (Exception eee) {
                            // No chance
                        }
                    }
                } else {
                    handleServerError();
                    try {
                        connection.close();
                    } catch (Exception ee) {
                        // No chance ^2
                    }
                }
            }
        }
    }

    /**
     * Handles the HTTP query
     *
     * @throws IOException
     */
    protected abstract void handleQuery() throws Exception;

    /**
     * Handles a server error
     */
    protected void handleServerError() {
        // can be overwritten
    }

    /**
     * sets the HTTP status
     *
     * @param statusCode
     *            the HTTP status code
     * @param statusDescription
     *            the HTTP status description
     */
    protected void setResponseStatus(int statusCode, String statusDescription) {
        responseStatusCode = statusCode;
        responseStatusDescription = statusDescription;
    }

    /**
     * sets a HTTP header
     *
     * @param headerName
     *            the name
     * @param headerValue
     *            the value
     */
    protected void setHeader(String headerName, String headerValue) {
        responseHeaders.put(headerName, headerValue);
    }

    /**
     * unsets a HTTP header
     *
     * @param headerName
     *            the name
     */
    protected void deleteHeader(String headerName) {
        responseHeaders.remove(headerName);
    }

    /**
     * sends all headers followed by empty line
     *
     * @throws IOException
     */
    protected void sendHeaders() throws IOException {
        connection.sendHeader("HTTP/1.1 " + responseStatusCode + " "
                + responseStatusDescription + "\r\n");
        for (Map.Entry<String, String> header : responseHeaders.entrySet()) {
            connection.sendHeader(header.getKey() + ": " + header.getValue()
                    + "\r\n");
        }
        connection.sendHeader("\r\n");
        responseHeadersSent = true;
    }

    /**
     * If the headers are already sent
     *
     * @return true, if headers are sent
     */
    public boolean areHeadersSent() {
        return responseHeadersSent;
    }
}
